use std::ops::Add;

pub trait Measure {
    fn measure_as_u32(&self) -> u32;
}

impl Measure for u32 {
    #[inline]
    fn measure_as_u32(&self) -> u32 {
        *self
    }
}

pub enum BottomUpMeasurableFullBiTree<T: Measure> {
    Node {
        measure: u32,
        lc: Box<BottomUpMeasurableFullBiTree<T>>,
        rc: Box<BottomUpMeasurableFullBiTree<T>>
    },
    Leave(T)
}

pub struct RefToMeasurableWrapper<'a, T: Measure> { 
    p: &'a T 
}

impl<'a, T: Measure> Measure for RefToMeasurableWrapper<'a, T> {
    fn measure_as_u32(&self) -> u32 {
        self.p.measure_as_u32()
    }
}

impl<T: Measure> Measure for BottomUpMeasurableFullBiTree<T> {
    fn measure_as_u32(&self) -> u32 {
        match self {
            BottomUpMeasurableFullBiTree::Node { measure, lc: _, rc: _ } => *measure,
            BottomUpMeasurableFullBiTree::Leave(l) => l.measure_as_u32()
        }
    }
}

impl<T: Measure> Add for BottomUpMeasurableFullBiTree<T> {
    type Output = Self;

    fn add(self, rhs: Self) -> Self::Output {
        Self::Node { 
            measure: self.measure_as_u32() + rhs.measure_as_u32(), 
            lc: Box::new(self), 
            rc: Box::new(rhs) 
        }
    }
}

impl<T: Measure> PartialEq for BottomUpMeasurableFullBiTree<T> {
    fn eq(&self, other: &Self) -> bool {
        self.measure_as_u32() == other.measure_as_u32()
    }
}

impl<T: Measure> Eq for BottomUpMeasurableFullBiTree<T> { }

impl<T: Measure> PartialOrd for BottomUpMeasurableFullBiTree<T> {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl<T: Measure> Ord for BottomUpMeasurableFullBiTree<T> {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.measure_as_u32().cmp(&other.measure_as_u32())
    }
}